package com.nowcoder.community.service;

import com.nowcoder.community.entity.User;
import com.nowcoder.community.util.CommunityConstant;
import com.nowcoder.community.util.RedisKeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @ClassName FollowService
 * @Description
 * @Author hhwu
 * @Date 2022/5/26 10:11
 * Version 1.0
 **/
@Service
public class FollowService implements CommunityConstant {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserService userService;


    public void follow(int userId, int entityType, int entityId) {
        /**
         * @Author hhwu
         * @Description //TODO 新增关注,同时处理关注者和被关注者两份数据
         * @Date 10:26 2022/5/26
         * @Param [userId, entityType, entityId]
         * @return void
         **/
        // 编程式事务
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
                String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);

                operations.multi();

                operations.opsForZSet().add(followeeKey, entityId, System.currentTimeMillis());// 当前用户关注了一个人
                operations.opsForZSet().add(followerKey, userId, System.currentTimeMillis());// 当前用户增加了一个粉丝

                return operations.exec();
            }
        });
    }

    public void unfollow(int userId, int entityType, int entityId) {
        /**
         * @Author hhwu
         * @Description //TODO 减少关注，同时处理被关注者和关注者两份数据
         * @Date 10:27 2022/5/26
         * @Param [userId, entityType, entityId]
         * @return void
         **/
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
                String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);

                operations.multi();

                operations.opsForZSet().remove(followeeKey, entityId);// 当前用户取消关注了一个人
                operations.opsForZSet().remove(followerKey, userId);// 当前用户减少了一个粉丝

                return operations.exec();
            }
        });
    }

    public long findFolloweeCount(int userId, int entityType) {
        /**
         * @Author hhwu
         * @Description //TODO 查询该用户关注的实体的数量
         * @Date 11:15 2022/5/26
         * @Param [userId, entityType]
         * @return long
         **/
        String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
        return redisTemplate.opsForZSet().zCard(followeeKey);
    }

    public long findFollowerCount(int entityType, int entityId) {
        /**
         * @Author hhwu
         * @Description //TODO 查询实体的粉丝数量
         * @Date 11:17 2022/5/26
         * @Param [entityType, entityId]
         * @return long
         **/
        String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
        return redisTemplate.opsForZSet().zCard(followerKey);
    }

    public boolean hasFollowed(int userId, int entityType, int entityId) {
        /**
         * @Author hhwu
         * @Description //TODO 查询该当前用户是否关注某实体
         * @Date 11:18 2022/5/26
         * @Param []
         * @return
         **/
        String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
        return redisTemplate.opsForZSet().score(followeeKey, entityId) != null;
    }

    public List<Map<String, Object>> findFollowees(int userId, int offset, int limit) {
        /**
         * @Author hhwu
         * @Description //TODO 查询某用户（不一定是当前登录用户）关注的人
         * @Date 14:54 2022/5/26
         * @Param [userId, offset, limit]
         * @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
         **/
        String followeeKey = RedisKeyUtil.getFolloweeKey(userId, ENTITY_TYPE_USER);
//        虽然java中的set集合的实现类都是无序的，但是redis对set接口又重新进行了实现，是有序的
        Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followeeKey, offset, offset + limit - 1);

        if (targetIds == null) {
            return null;
        }

        List<Map<String, Object>> list = new ArrayList<>();
        for (Integer targetId : targetIds) {
            Map<String, Object> map = new HashMap<>();
            User user = userService.findUserById(targetId);
            map.put("user", user);
            Double score = redisTemplate.opsForZSet().score(followeeKey, targetId);
            map.put("followTime", new Date(score.longValue()));
            list.add(map);
        }

        return list;
    }

    public List<Map<String, Object>> findFollowers(int userId, int offset, int limit) {
        /**
         * @Author hhwu
         * @Description //TODO 查询某用户（不一定是当前登录用户）的粉丝
         * @Date 14:55 2022/5/26
         * @Param [userId, offset, limit]
         * @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
         **/
        String followerKey = RedisKeyUtil.getFollowerKey(ENTITY_TYPE_USER, userId);
        Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followerKey, offset, offset + limit - 1);

        if (targetIds == null) {
            return null;
        }

        List<Map<String, Object>> list = new ArrayList<>();
        for (Integer targetId : targetIds) {
            Map<String, Object> map = new HashMap<>();
            User user = userService.findUserById(targetId);
            map.put("user", user);
            Double score = redisTemplate.opsForZSet().score(followerKey, targetId);
            map.put("followTime", new Date(score.longValue()));
            list.add(map);
        }

        return list;
    }
}
